package com.swak.boot.config.vertx;

import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;

import com.swak.annotation.Flux;
import com.swak.annotation.FluxService;
import com.swak.annotation.RestService;
import com.swak.vertx.config.ServiceBean;

/**
 * 处理服务Bean，且需要定义最后处理
 * 
 * @author lifeng
 * @date 2020年8月23日 下午9:35:04
 */
@Order(Ordered.LOWEST_PRECEDENCE)
public class ServiceAnnotationBeanPostProcessor implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {

	private Environment environment;

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			ClassLoader loader = beanFactory.getBeanClassLoader();
			String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
			for (String beanDefinitionName : beanDefinitionNames) {
				BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
				Class<?> beanClass = null;
				AnnotationMetadata metadata = null;

				// 通过 @Service注入
				if (beanDefinition instanceof ScannedGenericBeanDefinition
						&& ((AbstractBeanDefinition) beanDefinition).getBeanClassName() != null) {
					beanClass = ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(loader);
					metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
				}

				// 通过@Bean 注入: ConfigurationClassBeanDefinition 是一个私有的类，不然直接可以使用此类型
				else if (beanDefinition instanceof RootBeanDefinition
						&& beanDefinition instanceof AnnotatedBeanDefinition
						&& ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata() != null
						&& ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata().getAnnotations()
								.isDirectlyPresent(Flux.class)) {
					beanClass = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata().getAnnotations()
							.get(Flux.class).getClass("value");
					metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
				}

				// 是服务，则注入服务
				if (beanClass != null && this.isServie(metadata, beanClass)) {
					this.registerServiceBeanDefinition((DefaultListableBeanFactory) beanFactory, beanDefinitionName,
							beanClass);
				}
			}
		} catch (Exception e) {
			throw new IllegalArgumentException("响应式服务注解异常", e);
		}
	}

	private void registerServiceBeanDefinition(DefaultListableBeanFactory beanFactory, String beanDefinitionName,
			Class<?> beanClass) {
		// FluxService mapping = AnnotatedElementUtils.findMergedAnnotation(beanClass,
		// FluxService.class);

		Class<?>[] classes = ClassUtils.getAllInterfacesForClass(beanClass);
		if (classes.length == 0) {
			classes = new Class<?>[] { beanClass };
		}

		boolean exported = false;
		for (Class<?> inter : classes) {
			if (inter.getName().startsWith("org.springframework.")) {
				continue;
			}
			AbstractBeanDefinition serviceBeanDefinition = this.registryBeans(beanClass, inter, beanDefinitionName);
			beanFactory.registerBeanDefinition(this.generateServiceBeanName(beanDefinitionName, inter),
					serviceBeanDefinition);
			exported = true;
		}

		/*
		 * bug: 由于配置了接口导致没有发布，默认发布服务类的服務
		 */
		if (!exported) {
			AbstractBeanDefinition serviceBeanDefinition = this.registryBeans(beanClass, beanClass, beanDefinitionName);
			beanFactory.registerBeanDefinition(this.generateServiceBeanName(beanDefinitionName, beanClass),
					serviceBeanDefinition);
		}
	}

	private String generateServiceBeanName(String beanName, Class<?> interfaceClass) {
		return new StringBuilder("@FluxService").append(" ").append(interfaceClass.getName()).toString();
	}

	private AbstractBeanDefinition registryBeans(Class<?> beanClass, Class<?> interClass, String beanName) {
		BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
		String resolvedBeanName = environment.resolvePlaceholders(beanName);
		builder.addPropertyReference("ref", resolvedBeanName);
		builder.addPropertyValue("beanClass", beanClass);
		builder.addPropertyValue("interClass", interClass);
		return builder.getBeanDefinition();
	}

	/**
	 * 这里在底层是通过 HashMap存储，所以 isAnnotationPresent 会很高效
	 * 
	 * 所有的自定义的 FluxService 需要添加 Flux 注解， 因为这里为了提高性能没有一层一层的解析注解
	 * 
	 * @param clazz
	 * @return
	 */
	protected boolean isServie(AnnotationMetadata metadata, Class<?> clazz) {

		// 直接通过申明的注解判断
		boolean isService = (clazz.isAnnotationPresent(FluxService.class)
				|| clazz.isAnnotationPresent(RestService.class) || clazz.isAnnotationPresent(Flux.class));

		// 通过 Spring 收集的注解来判断
		if (!isService && metadata != null) {
		}
		return isService;
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
	}
}